{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 简化的memcached\n",
    "- 处理多个客户端发过来的请求\n",
    "- 采用非阻塞IO的模型"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# What is memcached?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- First generation of in-memory caching in web era\n",
    "  - Probably the oldest NoSQL, besides BDB"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- Non-persistent, remote, hash table with LRU\n",
    "  - Simple key/value store\n",
    "  - Plus some other features like counter"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- Very simple failuremodel\n",
    "  - Unlike a database\n",
    "- Largely deprecated by other more advanced NoSQL products"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 2000左右非常流行, 第一代nosql, kv值存储, LRU(丢弃最久未访问数据, 从而控制内存使用量), 计数器"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Why reimplement memcached?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- Simple text based request-response protocol\n",
    "- 简单的文本协议, 可以使用telnet与服务器交互, 不用单独写客户端"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- Serve concurrent clients with multiple IO threads\n",
    "  - Scalable IO model: one-loop-per-thread, not thread per connection\n",
    "- 请求响应式协议, 一问一答"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- Concurrent requests accessing shared state\n",
    "  - Efficient synchronization by sharding locks\n",
    "- 处理来自并发的请求, 多个客户端会连接到memcached(对外是个Web服务器, 对内是个客户端)\n",
    "- 服务器一个进程, 客户端很多个进程; procmon是框架(网络库)处理"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- Memory intensive\n",
    "- memcached的并发请求会访问共享数据, 也就是两个客户端会相互影响, 所以需要适当的同步, 减少锁挣用, memcache是一个多线程程序, 使用单线成实现对于多核时代这是开历史倒车\n",
    "- Sudoku每个连接是独立的, 没有共享数据, 见书6.6节\n",
    "- memcached是内存密集型应用, ttcp, netcat是IO密集型, procmon生成CPU曲线是CPU密集的; IO隔离度最差, CPU隔离度较好, 内存密集型隔离度最好. \n",
    "- 隔离度: 内存密集度型, 只要内存够大, 一台机器启动2个进程, 两个进程不互相影响; IO密集型会争抢资源"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- Online profiling with gperftools\n",
    "- 对生产环境的系统做在线的测试"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- Small code base: 1 kloc, excluding networking"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Basic protocol"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- memcached可以简单视为一个远程的hashtabble"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- Storage (set/add/replace/append/prepend)\n",
    "\n",
    "```\n",
    "set <key> <flags> <exptime> <bytes> [noreply]\\r\\n\n",
    "\n",
    "<data block>\\r\\n\n",
    "\n",
    "STORED\\r\\n\n",
    "```\n",
    "\n",
    "- 存储"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- Retrieval (get/gets)\n",
    "\n",
    "```\n",
    "get <key>*\\r\\n\n",
    "\n",
    "VALUE <key> <flags> <bytes> [<cas unique>]\\r\\n\n",
    "\n",
    "<data block>\\r\\n\n",
    "\n",
    "END\\r\\n\n",
    "```\n",
    "\n",
    "- 取回"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- Deletion\n",
    "\n",
    "```\n",
    "delete <key> [noreply]\\r\\n\n",
    "\n",
    "DELETED\\r\\n\n",
    "```\n",
    "\n",
    "- 删除"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- Compare-and-Swap\n",
    "- CAS"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 多个客户端发送请求会怎么样?\n",
    "  - 例如: 一个客户端get, 另一个客户端delete. 是返回报错, 还是返回删除一半的值.还是返回不存在?\n",
    "  - 例如: 一个客户端get, 另一个客户端set. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 运行"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```shell\n",
    "bin/memcached_debug\n",
    "```\n",
    "\n",
    "```shell\n",
    "telnet local 11211\n",
    "...\n",
    "\n",
    "varsion\n",
    "\n",
    "delete now\n",
    "\n",
    "get now\n",
    "\n",
    "set now 0 0 10\n",
    "\n",
    "get now\n",
    "\n",
    "set now 43 0 8\n",
    "\n",
    "get now\n",
    "\n",
    "delete now\n",
    "\n",
    "delete now\n",
    "\n",
    "memstat\n",
    "\n",
    "quit\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Features supported"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- Text protocol"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- Commands\n",
    "  - get/gets/delete/version/memstat/quit/shutdown\n",
    "  - set/add/replace/append/prepend/cas"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- The most difficult command to support:\n",
    "  - shutdown\n",
    "- 多线程服务端程序中, 干净的退出是一个很不常见的需求, 资源释放的顺序很关键,class的成员可能需要调整位置,防止引用失效"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- Online profiling"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- Pass as much unit tests aspossible"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 单元测试"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 修改memcached-debug, 指向moduo版本的memcached_debug\n",
    "\n",
    "```shell\n",
    "prove t/getset.t\n",
    "```\n",
    "\n",
    "```shell\n",
    "top\n",
    "```\n",
    "\n",
    "```shell\n",
    "prove t/lru.t\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Features not supported"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- Binary Protocol"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- UDP transport"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- Customized SLAB memory allocator"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- LRU (Least Recently Used) replacementalgorithm\n",
    "  - Memory usage is not bounded\n",
    "- 内存不受限"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- Expiration\n",
    "- 没有超时"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- Commands:\n",
    "  - incr/decr/touch/stats/others"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 简化版本主要为了展示网络编程"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 代码阅读-数据结构部分 *"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- https://github.com/chenshuo/muduo/tree/master/examples/memcached"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "cpp"
    }
   },
   "outputs": [],
   "source": [
    "// https://github.com/chenshuo/muduo/blob/master/examples/memcached/server/Item.h\n",
    "\n",
    "#ifndef MUDUO_EXAMPLES_MEMCACHED_SERVER_ITEM_H\n",
    "#define MUDUO_EXAMPLES_MEMCACHED_SERVER_ITEM_H\n",
    "\n",
    "#include \"muduo/base/Atomic.h\"\n",
    "#include \"muduo/base/StringPiece.h\"\n",
    "#include \"muduo/base/Types.h\"\n",
    "\n",
    "#include <memory>\n",
    "\n",
    "namespace muduo\n",
    "{\n",
    "namespace net\n",
    "{\n",
    "class Buffer;\n",
    "}\n",
    "}\n",
    "\n",
    "class Item;\n",
    "typedef std::shared_ptr<Item> ItemPtr;  // TODO: use unique_ptr\n",
    "typedef std::shared_ptr<const Item> ConstItemPtr;  // TODO: use unique_ptr\n",
    "\n",
    "// Item is immutable once added into hash table\n",
    "class Item : muduo::noncopyable\n",
    "{\n",
    " public:\n",
    "  enum UpdatePolicy\n",
    "  {\n",
    "    kInvalid,\n",
    "    kSet,\n",
    "    kAdd,\n",
    "    kReplace,\n",
    "    kAppend,\n",
    "    kPrepend,\n",
    "    kCas,\n",
    "  };\n",
    "\n",
    "  static ItemPtr makeItem(muduo::StringPiece keyArg,\n",
    "                          uint32_t flagsArg,\n",
    "                          int exptimeArg,\n",
    "                          int valuelen,\n",
    "                          uint64_t casArg)\n",
    "  {\n",
    "    return std::make_shared<Item>(keyArg, flagsArg, exptimeArg, valuelen, casArg);\n",
    "    //return ItemPtr(new Item(keyArg, flagsArg, exptimeArg, valuelen, casArg));\n",
    "  }\n",
    "\n",
    "  Item(muduo::StringPiece keyArg,\n",
    "       uint32_t flagsArg,\n",
    "       int exptimeArg,\n",
    "       int valuelen,\n",
    "       uint64_t casArg);\n",
    "\n",
    "  ~Item()\n",
    "  {\n",
    "    ::free(data_);\n",
    "  }\n",
    "\n",
    "  muduo::StringPiece key() const\n",
    "  {\n",
    "    return muduo::StringPiece(data_, keylen_);\n",
    "  }\n",
    "\n",
    "  uint32_t flags() const\n",
    "  {\n",
    "    return flags_;\n",
    "  }\n",
    "\n",
    "  int rel_exptime() const\n",
    "  {\n",
    "    return rel_exptime_;\n",
    "  }\n",
    "\n",
    "  const char* value() const\n",
    "  {\n",
    "    return data_+keylen_;\n",
    "  }\n",
    "\n",
    "  size_t valueLength() const\n",
    "  {\n",
    "    return valuelen_;\n",
    "  }\n",
    "\n",
    "  uint64_t cas() const\n",
    "  {\n",
    "    return cas_;\n",
    "  }\n",
    "\n",
    "  size_t hash() const\n",
    "  {\n",
    "    return hash_;\n",
    "  }\n",
    "\n",
    "  void setCas(uint64_t casArg)\n",
    "  {\n",
    "    cas_ = casArg;\n",
    "  }\n",
    "\n",
    "  size_t neededBytes() const\n",
    "  {\n",
    "    return totalLen() - receivedBytes_;\n",
    "  }\n",
    "\n",
    "  void append(const char* data, size_t len);\n",
    "\n",
    "  bool endsWithCRLF() const\n",
    "  {\n",
    "    return receivedBytes_ == totalLen()\n",
    "        && data_[totalLen()-2] == '\\r'\n",
    "        && data_[totalLen()-1] == '\\n';\n",
    "  }\n",
    "\n",
    "  void output(muduo::net::Buffer* out, bool needCas = false) const;\n",
    "\n",
    "  void resetKey(muduo::StringPiece k);\n",
    "\n",
    " private:\n",
    "  int totalLen() const { return keylen_ + valuelen_; }\n",
    "\n",
    "  // 48byte \n",
    "  int            keylen_; // uint8\n",
    "  const uint32_t flags_; \n",
    "  const int      rel_exptime_; // 超时时间\n",
    "  const int      valuelen_;\n",
    "  int            receivedBytes_;  // FIXME: remove this member\n",
    "  uint64_t       cas_;\n",
    "  size_t         hash_;\n",
    "  char*          data_;\n",
    "};\n",
    "\n",
    "#endif  // MUDUO_EXAMPLES_MEMCACHED_SERVER_ITEM_H"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "cpp"
    }
   },
   "outputs": [],
   "source": [
    "// https://github.com/chenshuo/muduo/blob/master/examples/memcached/server/Item.cc\n",
    "\n",
    "#include \"examples/memcached/server/Item.h\"\n",
    "\n",
    "#include \"muduo/base/LogStream.h\"\n",
    "#include \"muduo/net/Buffer.h\"\n",
    "\n",
    "#include <boost/functional/hash/hash.hpp>\n",
    "\n",
    "#include <string.h> // memcpy\n",
    "#include <stdio.h>\n",
    "\n",
    "using namespace muduo;\n",
    "using namespace muduo::net;\n",
    "\n",
    "Item::Item(StringPiece keyArg,\n",
    "           uint32_t flagsArg,\n",
    "           int exptimeArg,\n",
    "           int valuelen,\n",
    "           uint64_t casArg)\n",
    "  : keylen_(keyArg.size()),\n",
    "    flags_(flagsArg),\n",
    "    rel_exptime_(exptimeArg),\n",
    "    valuelen_(valuelen),\n",
    "    receivedBytes_(0),\n",
    "    cas_(casArg),\n",
    "    hash_(boost::hash_range(keyArg.begin(), keyArg.end())),\n",
    "    data_(static_cast<char*>(::malloc(totalLen())))\n",
    "{\n",
    "  assert(valuelen_ >= 2);\n",
    "  assert(receivedBytes_ < totalLen());\n",
    "  append(keyArg.data(), keylen_);\n",
    "}\n",
    "\n",
    "void Item::append(const char* data, size_t len)\n",
    "{\n",
    "  assert(len <= neededBytes());\n",
    "  memcpy(data_ + receivedBytes_, data, len);\n",
    "  receivedBytes_ += static_cast<int>(len);\n",
    "  assert(receivedBytes_ <= totalLen());\n",
    "}\n",
    "\n",
    "void Item::output(Buffer* out, bool needCas) const\n",
    "{\n",
    "  out->append(\"VALUE \");\n",
    "  out->append(data_, keylen_);\n",
    "  LogStream buf;\n",
    "  buf << ' ' << flags_ << ' ' << valuelen_-2;\n",
    "  if (needCas)\n",
    "  {\n",
    "    buf << ' ' << cas_;\n",
    "  }\n",
    "  buf << \"\\r\\n\";\n",
    "  out->append(buf.buffer().data(), buf.buffer().length());\n",
    "  out->append(value(), valuelen_);\n",
    "}\n",
    "\n",
    "void Item::resetKey(StringPiece k)\n",
    "{\n",
    "  assert(k.size() <= 250);\n",
    "  keylen_ = k.size();\n",
    "  receivedBytes_ = 0;\n",
    "  append(k.data(), k.size());\n",
    "  hash_ = boost::hash_range(k.begin(), k.end());\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "cpp"
    }
   },
   "outputs": [],
   "source": [
    "// https://github.com/chenshuo/muduo/blob/master/examples/memcached/server/MemcacheServer.h\n",
    "\n",
    "#ifndef MUDUO_EXAMPLES_MEMCACHED_SERVER_MEMCACHESERVER_H\n",
    "#define MUDUO_EXAMPLES_MEMCACHED_SERVER_MEMCACHESERVER_H\n",
    "\n",
    "#include \"examples/memcached/server/Item.h\"\n",
    "#include \"examples/memcached/server/Session.h\"\n",
    "\n",
    "#include \"muduo/base/Mutex.h\"\n",
    "#include \"muduo/net/TcpServer.h\"\n",
    "#include \"examples/wordcount/hash.h\"\n",
    "\n",
    "#include <array>\n",
    "#include <unordered_map>\n",
    "#include <unordered_set>\n",
    "\n",
    "class MemcacheServer : muduo::noncopyable\n",
    "{\n",
    " public:\n",
    "  struct Options\n",
    "  {\n",
    "    Options();\n",
    "    uint16_t tcpport;\n",
    "    uint16_t udpport;\n",
    "    uint16_t gperfport;\n",
    "    int threads;\n",
    "  };\n",
    "\n",
    "  MemcacheServer(muduo::net::EventLoop* loop, const Options&);\n",
    "  ~MemcacheServer();\n",
    "\n",
    "  void setThreadNum(int threads) { server_.setThreadNum(threads); }\n",
    "  void start();\n",
    "  void stop();\n",
    "\n",
    "  time_t startTime() const { return startTime_; }\n",
    "\n",
    "  bool storeItem(const ItemPtr& item, Item::UpdatePolicy policy, bool* exists); // 存\n",
    "  ConstItemPtr getItem(const ConstItemPtr& key) const; // 取\n",
    "  bool deleteItem(const ConstItemPtr& key); // 删\n",
    "\n",
    " private:\n",
    "  void onConnection(const muduo::net::TcpConnectionPtr& conn);\n",
    "\n",
    "  struct Stats;\n",
    "\n",
    "  muduo::net::EventLoop* loop_;  // not own\n",
    "  Options options_;\n",
    "  const time_t startTime_;\n",
    "\n",
    "  mutable muduo::MutexLock mutex_;\n",
    "  std::unordered_map<string, SessionPtr> sessions_ GUARDED_BY(mutex_);\n",
    "\n",
    "  // a complicated solution to save memory\n",
    "  struct Hash\n",
    "  {\n",
    "    size_t operator()(const ConstItemPtr& x) const\n",
    "    {\n",
    "      return x->hash();\n",
    "    }\n",
    "  };\n",
    "\n",
    "  struct Equal\n",
    "  {\n",
    "    bool operator()(const ConstItemPtr& x, const ConstItemPtr& y) const\n",
    "    {\n",
    "      return x->key() == y->key(); // 计算hash相等\n",
    "    }\n",
    "  };\n",
    "\n",
    "  typedef std::unordered_set<ConstItemPtr, Hash, Equal> ItemMap;\n",
    "\n",
    "  struct MapWithLock // 分片\n",
    "  {\n",
    "    ItemMap items;\n",
    "    mutable muduo::MutexLock mutex;\n",
    "  };\n",
    "\n",
    "  const static int kShards = 4096;\n",
    "\n",
    "  std::array<MapWithLock, kShards> shards_;\n",
    "\n",
    "  // NOT guarded by mutex_, but here because server_ has to destructs before\n",
    "  // sessions_\n",
    "  muduo::net::TcpServer server_; // shutdown: 先析构sessions_会crash\n",
    "  std::unique_ptr<Stats> stats_ PT_GUARDED_BY(mutex_);\n",
    "};\n",
    "\n",
    "#endif  // MUDUO_EXAMPLES_MEMCACHED_SERVER_MEMCACHESERVER_H"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "cpp"
    }
   },
   "outputs": [],
   "source": [
    "// https://github.com/chenshuo/muduo/blob/master/examples/memcached/server/MemcacheServer.cc\n",
    "\n",
    "#include \"examples/memcached/server/MemcacheServer.h\"\n",
    "\n",
    "#include \"muduo/base/Atomic.h\"\n",
    "#include \"muduo/base/Logging.h\"\n",
    "#include \"muduo/net/EventLoop.h\"\n",
    "\n",
    "using namespace muduo;\n",
    "using namespace muduo::net;\n",
    "\n",
    "muduo::AtomicInt64 g_cas;\n",
    "\n",
    "MemcacheServer::Options::Options()\n",
    "{\n",
    "  memZero(this, sizeof(*this));\n",
    "}\n",
    "\n",
    "struct MemcacheServer::Stats\n",
    "{\n",
    "};\n",
    "\n",
    "MemcacheServer::MemcacheServer(muduo::net::EventLoop* loop, const Options& options)\n",
    "  : loop_(loop),\n",
    "    options_(options),\n",
    "    startTime_(::time(NULL)-1),\n",
    "    server_(loop, InetAddress(options.tcpport), \"muduo-memcached\"),\n",
    "    stats_(new Stats)\n",
    "{\n",
    "  server_.setConnectionCallback(\n",
    "      std::bind(&MemcacheServer::onConnection, this, _1));\n",
    "}\n",
    "\n",
    "MemcacheServer::~MemcacheServer() = default;\n",
    "\n",
    "void MemcacheServer::start()\n",
    "{\n",
    "  server_.start();\n",
    "}\n",
    "\n",
    "void MemcacheServer::stop()\n",
    "{\n",
    "  loop_->runAfter(3.0, std::bind(&EventLoop::quit, loop_));\n",
    "}\n",
    "\n",
    "// 最复杂的操作\n",
    "bool MemcacheServer::storeItem(const ItemPtr& item, const Item::UpdatePolicy policy, bool* exists)\n",
    "{\n",
    "  assert(item->neededBytes() == 0);\n",
    "  MutexLock& mutex = shards_[item->hash() % kShards].mutex;\n",
    "  ItemMap& items = shards_[item->hash() % kShards].items;\n",
    "  MutexLockGuard lock(mutex); // 加锁\n",
    "  ItemMap::const_iterator it = items.find(item);\n",
    "  *exists = it != items.end(); // 存在\n",
    "  if (policy == Item::kSet)\n",
    "  {\n",
    "    item->setCas(g_cas.incrementAndGet());\n",
    "    if (*exists) // 已经存在: 删除, 然后插入\n",
    "    {\n",
    "      items.erase(it);\n",
    "    }\n",
    "    items.insert(item);\n",
    "  }\n",
    "  else\n",
    "  {\n",
    "    if (policy == Item::kAdd)\n",
    "    {\n",
    "      if (*exists)\n",
    "      {\n",
    "        return false;\n",
    "      }\n",
    "      else\n",
    "      {\n",
    "        item->setCas(g_cas.incrementAndGet());\n",
    "        items.insert(item);\n",
    "      }\n",
    "    }\n",
    "    else if (policy == Item::kReplace)\n",
    "    {\n",
    "      if (*exists)\n",
    "      {\n",
    "        item->setCas(g_cas.incrementAndGet());\n",
    "        items.erase(it); // hashset的insert, 原来存在, 不会进行替换\n",
    "        items.insert(item);\n",
    "      }\n",
    "      else\n",
    "      {\n",
    "        return false;\n",
    "      }\n",
    "    }\n",
    "    // 已经存在A, 然后两个客户, append了B, C, 结果是ABC或者ACB, 不能丢失数据\n",
    "    else if (policy == Item::kAppend || policy == Item::kPrepend)\n",
    "    {\n",
    "      if (*exists)\n",
    "      {\n",
    "        const ConstItemPtr& oldItem = *it;\n",
    "        int newLen = static_cast<int>(item->valueLength() + oldItem->valueLength() - 2);\n",
    "        ItemPtr newItem(Item::makeItem(item->key(),\n",
    "                                       oldItem->flags(),\n",
    "                                       oldItem->rel_exptime(),\n",
    "                                       newLen,\n",
    "                                       g_cas.incrementAndGet()));\n",
    "        if (policy == Item::kAppend)\n",
    "        {\n",
    "          newItem->append(oldItem->value(), oldItem->valueLength() - 2);\n",
    "          newItem->append(item->value(), item->valueLength());\n",
    "        }\n",
    "        else\n",
    "        {\n",
    "          newItem->append(item->value(), item->valueLength() - 2);\n",
    "          newItem->append(oldItem->value(), oldItem->valueLength());\n",
    "        }\n",
    "        assert(newItem->neededBytes() == 0);\n",
    "        assert(newItem->endsWithCRLF());\n",
    "        items.erase(it);\n",
    "        items.insert(newItem);\n",
    "      }\n",
    "      else\n",
    "      {\n",
    "        return false;\n",
    "      }\n",
    "    }\n",
    "    else if (policy == Item::kCas)\n",
    "    {\n",
    "      if (*exists && (*it)->cas() == item->cas())\n",
    "      {\n",
    "        item->setCas(g_cas.incrementAndGet());\n",
    "        items.erase(it);\n",
    "        items.insert(item);\n",
    "      }\n",
    "      else\n",
    "      {\n",
    "        return false;\n",
    "      }\n",
    "    }\n",
    "    else\n",
    "    {\n",
    "      assert(false);\n",
    "    }\n",
    "  }\n",
    "  return true;\n",
    "}\n",
    "\n",
    "ConstItemPtr MemcacheServer::getItem(const ConstItemPtr& key) const\n",
    "{\n",
    "  MutexLock& mutex = shards_[key->hash() % kShards].mutex;\n",
    "  const ItemMap& items = shards_[key->hash() % kShards].items;\n",
    "  MutexLockGuard lock(mutex);\n",
    "  ItemMap::const_iterator it = items.find(key);\n",
    "  return it != items.end() ? *it : ConstItemPtr(); // 不存在返回空的shardptr\n",
    "}\n",
    "\n",
    "bool MemcacheServer::deleteItem(const ConstItemPtr& key)\n",
    "{\n",
    "  MutexLock& mutex = shards_[key->hash() % kShards].mutex;\n",
    "  ItemMap& items = shards_[key->hash() % kShards].items;\n",
    "  MutexLockGuard lock(mutex);\n",
    "  return items.erase(key) == 1;\n",
    "}\n",
    "\n",
    "void MemcacheServer::onConnection(const TcpConnectionPtr& conn)\n",
    "{\n",
    "  if (conn->connected())\n",
    "  {\n",
    "    SessionPtr session(new Session(this, conn));\n",
    "    MutexLockGuard lock(mutex_);\n",
    "    assert(sessions_.find(conn->name()) == sessions_.end());\n",
    "    sessions_[conn->name()] = session;\n",
    "    // assert(sessions_.size() == stats_.current_conns);\n",
    "  }\n",
    "  else\n",
    "  {\n",
    "    MutexLockGuard lock(mutex_);\n",
    "    assert(sessions_.find(conn->name()) != sessions_.end());\n",
    "    sessions_.erase(conn->name());\n",
    "    // assert(sessions_.size() == stats_.current_conns);\n",
    "  }\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## footprint_test: 进程占资源多少"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "cpp"
    }
   },
   "outputs": [],
   "source": [
    "// https://github.com/chenshuo/muduo/blob/master/examples/memcached/server/footprint_test.cc\n",
    "\n",
    "#include \"examples/memcached/server/MemcacheServer.h\"\n",
    "#include \"muduo/net/EventLoop.h\"\n",
    "#include \"muduo/net/inspect/ProcessInspector.h\"\n",
    "\n",
    "#include <stdio.h>\n",
    "#ifdef HAVE_TCMALLOC\n",
    "#include <gperftools/heap-profiler.h>\n",
    "#include <gperftools/malloc_extension.h>\n",
    "#endif\n",
    "\n",
    "using namespace muduo::net;\n",
    "\n",
    "int main(int argc, char* argv[])\n",
    "{\n",
    "#ifdef HAVE_TCMALLOC\n",
    "  MallocExtension::Initialize();\n",
    "#endif\n",
    "  int items = argc > 1 ? atoi(argv[1]) : 10000; // 数据项\n",
    "  int keylen = argc > 2 ? atoi(argv[2]) : 10; // key的长度\n",
    "  int valuelen = argc > 3 ? atoi(argv[3]) : 100; // value的长度\n",
    "  EventLoop loop;\n",
    "  MemcacheServer::Options options;\n",
    "  MemcacheServer server(&loop, options);\n",
    "\n",
    "  printf(\"sizeof(Item) = %zd\\npid = %d\\nitems = %d\\nkeylen = %d\\nvaluelen = %d\\n\",\n",
    "         sizeof(Item), getpid(), items, keylen, valuelen);\n",
    "  char key[256] = { 0 };\n",
    "  string value;\n",
    "  for (int i = 0; i < items; ++i)\n",
    "  {\n",
    "    snprintf(key, sizeof key, \"%0*d\", keylen, i); // %0*d左侧补*个0\n",
    "    value.assign(valuelen, \"0123456789\"[i % 10]);\n",
    "    ItemPtr item(Item::makeItem(key, 0, 0, valuelen+2, 1));\n",
    "    item->append(value.data(), value.size());\n",
    "    item->append(\"\\r\\n\", 2);\n",
    "    assert(item->endsWithCRLF());\n",
    "    bool exists = false;\n",
    "    bool stored = server.storeItem(item, Item::kAdd, &exists); // 不走网络\n",
    "    assert(stored); (void) stored;\n",
    "    assert(!exists);\n",
    "  }\n",
    "  Inspector::ArgList arg;\n",
    "  printf(\"==========\\n%s\\n\",\n",
    "         ProcessInspector::overview(HttpRequest::kGet, arg).c_str());\n",
    "  // TODO: print bytes per item, overhead percent\n",
    "  fflush(stdout);\n",
    "#ifdef HAVE_TCMALLOC\n",
    "  char buf[8192];\n",
    "  MallocExtension::instance()->GetStats(buf, sizeof buf);\n",
    "  printf(\"%s\\n\", buf);\n",
    "  HeapProfilerDump(\"end\");\n",
    "\n",
    "/*\n",
    "  // only works for tcmalloc_debug\n",
    "  int blocks = 0;\n",
    "  size_t total = 0;\n",
    "  int histogram[kMallocHistogramSize] = { 0, };\n",
    "  MallocExtension::instance()->MallocMemoryStats(&blocks, &total, histogram);\n",
    "  printf(\"==========\\nblocks = %d\\ntotal = %zd\\n\", blocks, total);\n",
    "  for (int i = 0; i < kMallocHistogramSize; ++i)\n",
    "  {\n",
    "    printf(\"%d = %d\\n\", i, histogram[i]);\n",
    "  }\n",
    "*/\n",
    "#endif\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 测试"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- memcached_footprint_ptmalloc: libc\n",
    "- memcached_footprint_tcmalloc: libtcmalloc_and_profiler"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```shell\n",
    "./memcached_footprint_ptmalloc 1000000\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```shell\n",
    "./memcached_footprint_tcmalloc 1000000 # 每个item分配了3次内存\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```shell\n",
    "perf record ./memcached_footprint_ptmalloc 1000000 # 查看性能热点, 查看函数时间分配\n",
    "perf report\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```shell\n",
    "perf record ./memcached_footprint_tcmalloc 1000000 # 查看性能热点, 查看函数时间分配\n",
    "perf report\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "> 建议tcmalloc为默认内存分配器; 注意release与release版本对比"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## bench: 性能测试"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "cpp"
    }
   },
   "outputs": [],
   "source": [
    "// https://github.com/chenshuo/muduo/blob/master/examples/memcached/client/bench.cc\n",
    "\n",
    "#include \"muduo/base/CountDownLatch.h\"\n",
    "#include \"muduo/base/Logging.h\"\n",
    "#include \"muduo/net/EventLoop.h\"\n",
    "#include \"muduo/net/EventLoopThreadPool.h\"\n",
    "#include \"muduo/net/TcpClient.h\"\n",
    "\n",
    "#include <boost/program_options.hpp>\n",
    "#include <iostream>\n",
    "\n",
    "#include <stdio.h>\n",
    "\n",
    "namespace po = boost::program_options;\n",
    "using namespace muduo;\n",
    "using namespace muduo::net;\n",
    "\n",
    "class Client : noncopyable\n",
    "{\n",
    " public:\n",
    "  enum Operation\n",
    "  {\n",
    "    kGet,\n",
    "    kSet,\n",
    "  };\n",
    "\n",
    "  Client(const string& name,\n",
    "         EventLoop* loop,\n",
    "         const InetAddress& serverAddr,\n",
    "         Operation op,\n",
    "         int requests,\n",
    "         int keys,\n",
    "         int valuelen,\n",
    "         CountDownLatch* connected,\n",
    "         CountDownLatch* finished)\n",
    "    : name_(name),\n",
    "      client_(loop, serverAddr, name),\n",
    "      op_(op),\n",
    "      sent_(0),\n",
    "      acked_(0),\n",
    "      requests_(requests),\n",
    "      keys_(keys),\n",
    "      valuelen_(valuelen),\n",
    "      value_(valuelen_, 'a'),\n",
    "      connected_(connected),\n",
    "      finished_(finished)\n",
    "  {\n",
    "    value_ += \"\\r\\n\";\n",
    "    client_.setConnectionCallback(std::bind(&Client::onConnection, this, _1));\n",
    "    client_.setMessageCallback(std::bind(&Client::onMessage, this, _1, _2, _3));\n",
    "    client_.connect();\n",
    "  }\n",
    "\n",
    "  void send()\n",
    "  {\n",
    "    Buffer buf;\n",
    "    fill(&buf);\n",
    "    conn_->send(&buf);\n",
    "  }\n",
    "\n",
    " private:\n",
    "\n",
    "  void onConnection(const TcpConnectionPtr& conn)\n",
    "  {\n",
    "    if (conn->connected())\n",
    "    {\n",
    "      conn_ = conn;\n",
    "      connected_->countDown();\n",
    "    }\n",
    "    else\n",
    "    {\n",
    "      conn_.reset();\n",
    "      client_.getLoop()->queueInLoop(std::bind(&CountDownLatch::countDown, finished_));\n",
    "    }\n",
    "  }\n",
    "\n",
    "  void onMessage(const TcpConnectionPtr& conn,\n",
    "                 Buffer* buffer,\n",
    "                 Timestamp receiveTime)\n",
    "  {\n",
    "    if (op_ == kSet)\n",
    "    {\n",
    "      while (buffer->readableBytes() > 0)\n",
    "      {\n",
    "        const char* crlf = buffer->findCRLF();\n",
    "        if (crlf)\n",
    "        {\n",
    "          buffer->retrieveUntil(crlf+2);\n",
    "          ++acked_;\n",
    "          if (sent_ < requests_)\n",
    "          {\n",
    "            send();\n",
    "          }\n",
    "        }\n",
    "        else\n",
    "        {\n",
    "          break;\n",
    "        }\n",
    "      }\n",
    "    }\n",
    "    else\n",
    "    {\n",
    "      while (buffer->readableBytes() > 0)\n",
    "      {\n",
    "        const char* end = static_cast<const char*>(memmem(buffer->peek(),\n",
    "                                                          buffer->readableBytes(),\n",
    "                                                          \"END\\r\\n\", 5));\n",
    "        if (end)\n",
    "        {\n",
    "          buffer->retrieveUntil(end+5);\n",
    "          ++acked_;\n",
    "          if (sent_ < requests_)\n",
    "          {\n",
    "            send();\n",
    "          }\n",
    "        }\n",
    "        else\n",
    "        {\n",
    "          break;\n",
    "        }\n",
    "      }\n",
    "    }\n",
    "    if (acked_ == requests_)\n",
    "    {\n",
    "      conn_->shutdown();\n",
    "    }\n",
    "  }\n",
    "\n",
    "  void fill(Buffer* buf)\n",
    "  {\n",
    "    char req[256];\n",
    "    if (op_ == kSet)\n",
    "    {\n",
    "      snprintf(req, sizeof req, \"set %s%d 42 0 %d\\r\\n\", name_.c_str(), sent_ % keys_, valuelen_);\n",
    "      ++sent_;\n",
    "      buf->append(req);\n",
    "      buf->append(value_);\n",
    "    }\n",
    "    else\n",
    "    {\n",
    "      snprintf(req, sizeof req, \"get %s%d\\r\\n\", name_.c_str(), sent_ % keys_);\n",
    "      ++sent_;\n",
    "      buf->append(req);\n",
    "    }\n",
    "  }\n",
    "\n",
    "  string name_;\n",
    "  TcpClient client_;\n",
    "  TcpConnectionPtr conn_;\n",
    "  const Operation op_;\n",
    "  int sent_;\n",
    "  int acked_;\n",
    "  const int requests_;\n",
    "  const int keys_;\n",
    "  const int valuelen_;\n",
    "  string value_;\n",
    "  CountDownLatch* const connected_;\n",
    "  CountDownLatch* const finished_;\n",
    "};\n",
    "\n",
    "int main(int argc, char* argv[])\n",
    "{\n",
    "  Logger::setLogLevel(Logger::WARN);\n",
    "\n",
    "  uint16_t tcpport = 11211;\n",
    "  string hostIp = \"127.0.0.1\";\n",
    "  int threads = 4;\n",
    "  int clients = 100;\n",
    "  int requests = 100000;\n",
    "  int keys = 10000;\n",
    "  bool set = false;\n",
    "\n",
    "  po::options_description desc(\"Allowed options\");\n",
    "  desc.add_options()\n",
    "      (\"help,h\", \"Help\")\n",
    "      (\"port,p\", po::value<uint16_t>(&tcpport), \"TCP port\")\n",
    "      (\"ip,i\", po::value<string>(&hostIp), \"Host IP\")\n",
    "      (\"threads,t\", po::value<int>(&threads), \"Number of worker threads\")\n",
    "      (\"clients,c\", po::value<int>(&clients), \"Number of concurrent clients\")\n",
    "      (\"requests,r\", po::value<int>(&requests), \"Number of requests per clients\")\n",
    "      (\"keys,k\", po::value<int>(&keys), \"Number of keys per clients\")\n",
    "      (\"set,s\", \"Get or Set\")\n",
    "      ;\n",
    "\n",
    "  po::variables_map vm;\n",
    "  po::store(po::parse_command_line(argc, argv, desc), vm);\n",
    "  po::notify(vm);\n",
    "\n",
    "  if (vm.count(\"help\"))\n",
    "  {\n",
    "    std::cout << desc << \"\\n\";\n",
    "    return 0;\n",
    "  }\n",
    "  set = vm.count(\"set\");\n",
    "\n",
    "  InetAddress serverAddr(hostIp, tcpport);\n",
    "  LOG_WARN << \"Connecting \" << serverAddr.toIpPort();\n",
    "\n",
    "  EventLoop loop;\n",
    "  EventLoopThreadPool pool(&loop, \"bench-memcache\");\n",
    "\n",
    "  int valuelen = 100;\n",
    "  Client::Operation op = set ? Client::kSet : Client::kGet;\n",
    "\n",
    "  double memoryMiB = 1.0 * clients * keys * (32+80+valuelen+8) / 1024 / 1024; // 估算\n",
    "  LOG_WARN << \"estimated memcached-debug memory usage \" << int(memoryMiB) << \" MiB\";\n",
    "\n",
    "  pool.setThreadNum(threads);\n",
    "  pool.start();\n",
    "\n",
    "  char buf[32];\n",
    "  CountDownLatch connected(clients);\n",
    "  CountDownLatch finished(clients);\n",
    "  std::vector<std::unique_ptr<Client>> holder;\n",
    "  for (int i = 0; i < clients; ++i)\n",
    "  {\n",
    "    snprintf(buf, sizeof buf, \"%d-\", i+1);\n",
    "    holder.emplace_back(new Client(buf,\n",
    "                                pool.getNextLoop(),\n",
    "                                serverAddr,\n",
    "                                op,\n",
    "                                requests,\n",
    "                                keys,\n",
    "                                valuelen,\n",
    "                                &connected,\n",
    "                                &finished));\n",
    "  }\n",
    "  connected.wait();\n",
    "  LOG_WARN << clients << \" clients all connected\";\n",
    "  Timestamp start = Timestamp::now();\n",
    "  for (int i = 0; i < clients; ++i)\n",
    "  {\n",
    "    holder[i]->send();\n",
    "  }\n",
    "  finished.wait();\n",
    "  Timestamp end = Timestamp::now();\n",
    "  LOG_WARN << \"All finished\";\n",
    "  double seconds = timeDifference(end, start);\n",
    "  LOG_WARN << seconds << \" sec\";\n",
    "  LOG_WARN << 1.0 * clients * requests / seconds << \" QPS\";\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Item, Session, and Server"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- Prefer simplicity over performance\n",
    "  - Know when not to optimize\n",
    "- 有时候优化通过网络后感受不到, 内存操作时间 << 网络传输时间"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- Server keeps all Items in its hashtables"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- Session parses commands and calls Server to get/set/delete Items"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# IO models: blocking or non-blocking?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- Blocking IO, thread per connection\n",
    "\n",
    "| Feature             | Description                        |\n",
    "| ------------------- | ---------------------------------- |\n",
    "| Concurrency pattern | One thread per connection          |\n",
    "| Threads vs clients  | N threads for N clients            |\n",
    "| Suitable scale      | Good for ~100 clients              |\n",
    "| Memory usage        | Constant                           |\n",
    "\n",
    "- Non-blocking IO, event-based\n",
    "\n",
    "| Feature             | Description                           |\n",
    "| ------------------- | ------------------------------------- |\n",
    "| Concurrency pattern | Event-driven, non-blocking            |\n",
    "| Threads vs clients  | Fixed number of threads for N clients |\n",
    "| Suitable scale      | Good for ~1000s of clients            |\n",
    "| Memory usage        | Varies with workload                  |\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- linux下, 通常阻塞/非阻塞都是同步(select, epoll)的, aio/io_uring是异步的\n",
    "- 非阻塞IO, 内存缓冲区增加后不会自动缩小, 要节约内存, 要使用状态机; \n",
    "- 非阻塞与事件复用结合使用; 每个线程处理多个客户端; \n",
    "- 对于类似go的语言只支持thread per conn;\n",
    "- 客户端数量: (Little’s Law, 平均在制数 = 到达率 × 平均逗留时间) 1s来1W个客户, 平均每个只逗留100ms, 那到系统中的平均在制数量（并发数）=10000*0.1=1000\n",
    "- Web与memcached之间是通过连接池\n",
    "- lead-follower, epoll用来观察可读"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 代码阅读-网络部分*"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "cpp"
    }
   },
   "outputs": [],
   "source": [
    "// https://github.com/chenshuo/muduo/blob/master/examples/memcached/server/Session.h\n",
    "\n",
    "#ifndef MUDUO_EXAMPLES_MEMCACHED_SERVER_SESSION_H\n",
    "#define MUDUO_EXAMPLES_MEMCACHED_SERVER_SESSION_H\n",
    "\n",
    "#include \"examples/memcached/server/Item.h\"\n",
    "\n",
    "#include \"muduo/base/Logging.h\"\n",
    "\n",
    "#include \"muduo/net/TcpConnection.h\"\n",
    "\n",
    "#include <boost/tokenizer.hpp>\n",
    "\n",
    "using muduo::string;\n",
    "\n",
    "class MemcacheServer;\n",
    "\n",
    "class Session : public std::enable_shared_from_this<Session>,\n",
    "                muduo::noncopyable\n",
    "{\n",
    " public:\n",
    "  Session(MemcacheServer* owner, const muduo::net::TcpConnectionPtr& conn)\n",
    "    : owner_(owner),\n",
    "      conn_(conn),\n",
    "      state_(kNewCommand),\n",
    "      protocol_(kAscii), // FIXME\n",
    "      noreply_(false),\n",
    "      policy_(Item::kInvalid),\n",
    "      bytesToDiscard_(0),\n",
    "      needle_(Item::makeItem(kLongestKey, 0, 0, 2, 0)),\n",
    "      bytesRead_(0),\n",
    "      requestsProcessed_(0)\n",
    "  {\n",
    "    using std::placeholders::_1;\n",
    "    using std::placeholders::_2;\n",
    "    using std::placeholders::_3;\n",
    "\n",
    "    conn_->setMessageCallback(\n",
    "        std::bind(&Session::onMessage, this, _1, _2, _3));\n",
    "  }\n",
    "\n",
    "  ~Session()\n",
    "  {\n",
    "    LOG_INFO << \"requests processed: \" << requestsProcessed_\n",
    "             << \" input buffer size: \" << conn_->inputBuffer()->internalCapacity()\n",
    "             << \" output buffer size: \" << conn_->outputBuffer()->internalCapacity();\n",
    "  }\n",
    "\n",
    " private:\n",
    "  enum State\n",
    "  {\n",
    "    kNewCommand,\n",
    "    kReceiveValue,\n",
    "    kDiscardValue, // 超过1M字节\n",
    "  };\n",
    "\n",
    "  enum Protocol\n",
    "  {\n",
    "    kAscii,\n",
    "    kBinary,\n",
    "    kAuto,\n",
    "  };\n",
    "\n",
    "  void onMessage(const muduo::net::TcpConnectionPtr& conn,\n",
    "                 muduo::net::Buffer* buf,\n",
    "                 muduo::Timestamp);\n",
    "  void onWriteComplete(const muduo::net::TcpConnectionPtr& conn);\n",
    "  void receiveValue(muduo::net::Buffer* buf);\n",
    "  void discardValue(muduo::net::Buffer* buf);\n",
    "  // TODO: highWaterMark\n",
    "  // TODO: onWriteComplete\n",
    "\n",
    "  // returns true if finished a request\n",
    "  bool processRequest(muduo::StringPiece request);\n",
    "  void resetRequest();\n",
    "  void reply(muduo::StringPiece msg);\n",
    "\n",
    "  struct SpaceSeparator\n",
    "  {\n",
    "    void reset() {}\n",
    "    template <typename InputIterator, typename Token>\n",
    "    bool operator()(InputIterator& next, InputIterator end, Token& tok);\n",
    "  };\n",
    "\n",
    "  typedef boost::tokenizer<SpaceSeparator,\n",
    "      const char*,\n",
    "      muduo::StringPiece> Tokenizer;\n",
    "  struct Reader;\n",
    "  bool doUpdate(Tokenizer::iterator& beg, Tokenizer::iterator end);\n",
    "  void doDelete(Tokenizer::iterator& beg, Tokenizer::iterator end);\n",
    "\n",
    "  MemcacheServer* owner_;\n",
    "  muduo::net::TcpConnectionPtr conn_;\n",
    "  State state_;\n",
    "  Protocol protocol_;\n",
    "\n",
    "  // current request\n",
    "  string command_;\n",
    "  bool noreply_;\n",
    "  Item::UpdatePolicy policy_;\n",
    "  ItemPtr currItem_;\n",
    "  size_t bytesToDiscard_;\n",
    "  // cached\n",
    "  ItemPtr needle_;\n",
    "  muduo::net::Buffer outputBuf_;\n",
    "\n",
    "  // per session stats\n",
    "  size_t bytesRead_;\n",
    "  size_t requestsProcessed_;\n",
    "\n",
    "  static string kLongestKey;\n",
    "};\n",
    "\n",
    "typedef std::shared_ptr<Session> SessionPtr;\n",
    "\n",
    "#endif  // MUDUO_EXAMPLES_MEMCACHED_SERVER_SESSION_H"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "cpp"
    }
   },
   "outputs": [],
   "source": [
    "// https://github.com/chenshuo/muduo/blob/master/examples/memcached/server/Session.cc\n",
    "\n",
    "#include \"examples/memcached/server/Session.h\"\n",
    "#include \"examples/memcached/server/MemcacheServer.h\"\n",
    "\n",
    "#ifdef HAVE_TCMALLOC\n",
    "#include <gperftools/malloc_extension.h>\n",
    "#endif\n",
    "\n",
    "using namespace muduo;\n",
    "using namespace muduo::net;\n",
    "\n",
    "static bool isBinaryProtocol(uint8_t firstByte)\n",
    "{\n",
    "  return firstByte == 0x80;\n",
    "}\n",
    "\n",
    "const int kLongestKeySize = 250;\n",
    "string Session::kLongestKey(kLongestKeySize, 'x');\n",
    "\n",
    "template <typename InputIterator, typename Token>\n",
    "bool Session::SpaceSeparator::operator()(InputIterator& next, InputIterator end, Token& tok)\n",
    "{\n",
    "  while (next != end && *next == ' ')\n",
    "    ++next;\n",
    "  if (next == end)\n",
    "  {\n",
    "    tok.clear();\n",
    "    return false;\n",
    "  }\n",
    "  InputIterator start(next);\n",
    "  const char* sp = static_cast<const char*>(memchr(start, ' ', end - start));\n",
    "  if (sp)\n",
    "  {\n",
    "    tok.set(start, static_cast<int>(sp - start));\n",
    "    next = sp;\n",
    "  }\n",
    "  else\n",
    "  {\n",
    "    tok.set(start, static_cast<int>(end - next));\n",
    "    next = end;\n",
    "  }\n",
    "  return true;\n",
    "}\n",
    "\n",
    "struct Session::Reader\n",
    "{\n",
    "  Reader(Tokenizer::iterator& beg, Tokenizer::iterator end)\n",
    "      : first_(beg),\n",
    "        last_(end)\n",
    "  {\n",
    "  }\n",
    "\n",
    "  template<typename T>\n",
    "  bool read(T* val)\n",
    "  {\n",
    "    if (first_ == last_)\n",
    "      return false;\n",
    "    char* end = NULL;\n",
    "    uint64_t x = strtoull((*first_).data(), &end, 10);\n",
    "    if (end == (*first_).end())\n",
    "    {\n",
    "      *val = static_cast<T>(x);\n",
    "      ++first_;\n",
    "      return true;\n",
    "    }\n",
    "    return false;\n",
    "  }\n",
    "\n",
    " private:\n",
    "  Tokenizer::iterator first_;\n",
    "  Tokenizer::iterator last_;;\n",
    "};\n",
    "\n",
    "// 接受客户端消息\n",
    "void Session::onMessage(const muduo::net::TcpConnectionPtr& conn,\n",
    "                        muduo::net::Buffer* buf,\n",
    "                        muduo::Timestamp)\n",
    "\n",
    "{\n",
    "  const size_t initialReadable = buf->readableBytes();\n",
    "\n",
    "  while (buf->readableBytes() > 0)\n",
    "  {\n",
    "    if (state_ == kNewCommand)\n",
    "    {\n",
    "      if (protocol_ == kAuto)\n",
    "      {\n",
    "        assert(bytesRead_ == 0);\n",
    "        protocol_ = isBinaryProtocol(buf->peek()[0]) ? kBinary : kAscii;\n",
    "      }\n",
    "\n",
    "      assert(protocol_ == kAscii || protocol_ == kBinary);\n",
    "      if (protocol_ == kBinary)\n",
    "      {\n",
    "        // FIXME\n",
    "      }\n",
    "      else  // ASCII protocol\n",
    "      {\n",
    "        const char* crlf = buf->findCRLF(); // 以行为分割; 平方复杂度, 使用状态机代替\n",
    "        if (crlf)\n",
    "        {\n",
    "          int len = static_cast<int>(crlf - buf->peek());\n",
    "          StringPiece request(buf->peek(), len);\n",
    "          if (processRequest(request))\n",
    "          {\n",
    "            resetRequest();\n",
    "          }\n",
    "          buf->retrieveUntil(crlf + 2);\n",
    "        }\n",
    "        else\n",
    "        {\n",
    "          if (buf->readableBytes() > 1024)\n",
    "          {\n",
    "            // FIXME: check for 'get' and 'gets'\n",
    "            conn_->shutdown();\n",
    "            // buf->retrieveAll() ???\n",
    "          }\n",
    "          break; // while\n",
    "        }\n",
    "      }\n",
    "    }\n",
    "    else if (state_ == kReceiveValue)\n",
    "    {\n",
    "      receiveValue(buf);\n",
    "    }\n",
    "    else if (state_ == kDiscardValue) // 非阻塞IO复杂的地方\n",
    "    {\n",
    "      discardValue(buf);\n",
    "    }\n",
    "    else\n",
    "    {\n",
    "      assert(false);\n",
    "    }\n",
    "  }\n",
    "  bytesRead_ += initialReadable - buf->readableBytes();\n",
    "}\n",
    "\n",
    "void Session::receiveValue(muduo::net::Buffer* buf)\n",
    "{\n",
    "  assert(currItem_.get());\n",
    "  assert(state_ == kReceiveValue);\n",
    "  // if (protocol_ == kBinary)\n",
    "\n",
    "  const size_t avail = std::min(buf->readableBytes(), currItem_->neededBytes());\n",
    "  assert(currItem_.unique());\n",
    "  currItem_->append(buf->peek(), avail);\n",
    "  buf->retrieve(avail);\n",
    "  if (currItem_->neededBytes() == 0)\n",
    "  {\n",
    "    if (currItem_->endsWithCRLF())\n",
    "    {\n",
    "      bool exists = false;\n",
    "      if (owner_->storeItem(currItem_, policy_, &exists))\n",
    "      {\n",
    "        reply(\"STORED\\r\\n\");\n",
    "      }\n",
    "      else\n",
    "      {\n",
    "        if (policy_ == Item::kCas)\n",
    "        {\n",
    "          if (exists)\n",
    "          {\n",
    "            reply(\"EXISTS\\r\\n\");\n",
    "          }\n",
    "          else\n",
    "          {\n",
    "            reply(\"NOT_FOUND\\r\\n\");\n",
    "          }\n",
    "        }\n",
    "        else\n",
    "        {\n",
    "          reply(\"NOT_STORED\\r\\n\");\n",
    "        }\n",
    "      }\n",
    "    }\n",
    "    else\n",
    "    {\n",
    "      reply(\"CLIENT_ERROR bad data chunk\\r\\n\");\n",
    "    }\n",
    "    resetRequest();\n",
    "    state_ = kNewCommand;\n",
    "  }\n",
    "}\n",
    "\n",
    "void Session::discardValue(muduo::net::Buffer* buf)\n",
    "{\n",
    "  assert(!currItem_);\n",
    "  assert(state_ == kDiscardValue);\n",
    "  if (buf->readableBytes() < bytesToDiscard_)\n",
    "  {\n",
    "    bytesToDiscard_ -= buf->readableBytes();\n",
    "    buf->retrieveAll();\n",
    "  }\n",
    "  else\n",
    "  {\n",
    "    buf->retrieve(bytesToDiscard_);\n",
    "    bytesToDiscard_ = 0;\n",
    "    resetRequest();\n",
    "    state_ = kNewCommand;\n",
    "  }\n",
    "}\n",
    "\n",
    "bool Session::processRequest(StringPiece request)\n",
    "{\n",
    "  assert(command_.empty());\n",
    "  assert(!noreply_);\n",
    "  assert(policy_ == Item::kInvalid);\n",
    "  assert(!currItem_);\n",
    "  assert(bytesToDiscard_ == 0);\n",
    "  ++requestsProcessed_;\n",
    "\n",
    "  // check 'noreply' at end of request line\n",
    "  if (request.size() >= 8)\n",
    "  {\n",
    "    StringPiece end(request.end() - 8, 8);\n",
    "    if (end == \" noreply\")\n",
    "    {\n",
    "      noreply_ = true;\n",
    "      request.remove_suffix(8);\n",
    "    }\n",
    "  }\n",
    "\n",
    "  SpaceSeparator sep;\n",
    "  Tokenizer tok(request.begin(), request.end(), sep);\n",
    "  Tokenizer::iterator beg = tok.begin();\n",
    "  if (beg == tok.end())\n",
    "  {\n",
    "    reply(\"ERROR\\r\\n\");\n",
    "    return true;\n",
    "  }\n",
    "  (*beg).CopyToString(&command_);\n",
    "  ++beg;\n",
    "  if (command_ == \"set\" || command_ == \"add\" || command_ == \"replace\"\n",
    "      || command_ == \"append\" || command_ == \"prepend\" || command_ == \"cas\")\n",
    "  {\n",
    "    // this normally returns false\n",
    "    return doUpdate(beg, tok.end());\n",
    "  }\n",
    "  else if (command_ == \"get\" || command_ == \"gets\") // get\n",
    "  {\n",
    "    bool cas = command_ == \"gets\";\n",
    "\n",
    "    // FIXME: send multiple chunks with write complete callback. \n",
    "    while (beg != tok.end()) // 内存使用很粗略, outputBuffer增长后不一定会收缩\n",
    "    {\n",
    "      StringPiece key = *beg;\n",
    "      bool good = key.size() <= kLongestKeySize;\n",
    "      if (!good)\n",
    "      {\n",
    "        reply(\"CLIENT_ERROR bad command line format\\r\\n\");\n",
    "        return true;\n",
    "      }\n",
    "\n",
    "      needle_->resetKey(key);\n",
    "      ConstItemPtr item = owner_->getItem(needle_);\n",
    "      ++beg;\n",
    "      if (item)\n",
    "      {\n",
    "        item->output(&outputBuf_, cas);\n",
    "      }\n",
    "    }\n",
    "    outputBuf_.append(\"END\\r\\n\");\n",
    "\n",
    "    if (conn_->outputBuffer()->writableBytes() > 65536 + outputBuf_.readableBytes())\n",
    "    {\n",
    "      LOG_DEBUG << \"shrink output buffer from \" << conn_->outputBuffer()->internalCapacity();\n",
    "      conn_->outputBuffer()->shrink(65536 + outputBuf_.readableBytes());\n",
    "    }\n",
    "\n",
    "    conn_->send(&outputBuf_);\n",
    "  }\n",
    "  else if (command_ == \"delete\")\n",
    "  {\n",
    "    doDelete(beg, tok.end());\n",
    "  }\n",
    "  else if (command_ == \"version\")\n",
    "  {\n",
    "#ifdef HAVE_TCMALLOC\n",
    "    reply(\"VERSION 0.01 muduo with tcmalloc\\r\\n\");\n",
    "#else\n",
    "    reply(\"VERSION 0.01 muduo\\r\\n\");\n",
    "#endif\n",
    "  }\n",
    "#ifdef HAVE_TCMALLOC\n",
    "  else if (command_ == \"memstat\")\n",
    "  {\n",
    "    char buf[1024*64];\n",
    "    MallocExtension::instance()->GetStats(buf, sizeof buf);\n",
    "    reply(buf);\n",
    "  }\n",
    "#endif\n",
    "  else if (command_ == \"quit\")\n",
    "  {\n",
    "    conn_->shutdown();\n",
    "  }\n",
    "  else if (command_ == \"shutdown\")\n",
    "  {\n",
    "    // \"ERROR: shutdown not enabled\"\n",
    "    conn_->shutdown();\n",
    "    owner_->stop();\n",
    "  }\n",
    "  else\n",
    "  {\n",
    "    reply(\"ERROR\\r\\n\");\n",
    "    LOG_INFO << \"Unknown command: \" << command_;\n",
    "  }\n",
    "  return true;\n",
    "}\n",
    "\n",
    "void Session::resetRequest()\n",
    "{\n",
    "  command_.clear();\n",
    "  noreply_ = false;\n",
    "  policy_ = Item::kInvalid;\n",
    "  currItem_.reset();\n",
    "  bytesToDiscard_ = 0;\n",
    "}\n",
    "\n",
    "void Session::reply(muduo::StringPiece msg)\n",
    "{\n",
    "  if (!noreply_)\n",
    "  {\n",
    "    conn_->send(msg.data(), msg.size());\n",
    "  }\n",
    "}\n",
    "\n",
    "bool Session::doUpdate(Session::Tokenizer::iterator& beg, Session::Tokenizer::iterator end)\n",
    "{\n",
    "  if (command_ == \"set\")\n",
    "    policy_ = Item::kSet;\n",
    "  else if (command_ == \"add\")\n",
    "    policy_ = Item::kAdd;\n",
    "  else if (command_ == \"replace\")\n",
    "    policy_ = Item::kReplace;\n",
    "  else if (command_ == \"append\")\n",
    "    policy_ = Item::kAppend;\n",
    "  else if (command_ == \"prepend\")\n",
    "    policy_ = Item::kPrepend;\n",
    "  else if (command_ == \"cas\")\n",
    "    policy_ = Item::kCas;\n",
    "  else\n",
    "    assert(false);\n",
    "\n",
    "  // FIXME: check (beg != end)\n",
    "  StringPiece key = (*beg);\n",
    "  ++beg;\n",
    "  bool good = key.size() <= kLongestKeySize;\n",
    "\n",
    "  uint32_t flags = 0;\n",
    "  time_t exptime = 1;\n",
    "  int bytes = -1;\n",
    "  uint64_t cas = 0;\n",
    "\n",
    "  Reader r(beg, end);\n",
    "  good = good && r.read(&flags) && r.read(&exptime) && r.read(&bytes);\n",
    "\n",
    "  int rel_exptime = static_cast<int>(exptime);\n",
    "  if (exptime > 60*60*24*30)\n",
    "  {\n",
    "    rel_exptime = static_cast<int>(exptime - owner_->startTime());\n",
    "    if (rel_exptime < 1)\n",
    "    {\n",
    "      rel_exptime = 1;\n",
    "    }\n",
    "  }\n",
    "  else\n",
    "  {\n",
    "    // rel_exptime = exptime + currentTime;\n",
    "  }\n",
    "\n",
    "  if (good && policy_ == Item::kCas)\n",
    "  {\n",
    "    good = r.read(&cas);\n",
    "  }\n",
    "\n",
    "  if (!good)\n",
    "  {\n",
    "    reply(\"CLIENT_ERROR bad command line format\\r\\n\");\n",
    "    return true;\n",
    "  }\n",
    "  if (bytes > 1024*1024)\n",
    "  {\n",
    "    reply(\"SERVER_ERROR object too large for cache\\r\\n\");\n",
    "    needle_->resetKey(key);\n",
    "    owner_->deleteItem(needle_);\n",
    "    bytesToDiscard_ = bytes + 2;\n",
    "    state_ = kDiscardValue;\n",
    "    return false;\n",
    "  }\n",
    "  else\n",
    "  {\n",
    "    currItem_ = Item::makeItem(key, flags, rel_exptime, bytes + 2, cas);\n",
    "    state_ = kReceiveValue;\n",
    "    return false;\n",
    "  }\n",
    "}\n",
    "\n",
    "void Session::doDelete(Session::Tokenizer::iterator& beg, Session::Tokenizer::iterator end)\n",
    "{\n",
    "  assert(command_ == \"delete\");\n",
    "  // FIXME: check (beg != end)\n",
    "  StringPiece key = *beg;\n",
    "  bool good = key.size() <= kLongestKeySize;\n",
    "  ++beg;\n",
    "  if (!good)\n",
    "  {\n",
    "    reply(\"CLIENT_ERROR bad command line format\\r\\n\");\n",
    "  }\n",
    "  else if (beg != end && *beg != \"0\") // issue 108, old protocol\n",
    "  {\n",
    "    reply(\"CLIENT_ERROR bad command line format.  Usage: delete <key> [noreply]\\r\\n\");\n",
    "  }\n",
    "  else\n",
    "  {\n",
    "    needle_->resetKey(key); // 擦写变量\n",
    "    if (owner_->deleteItem(needle_))\n",
    "    {\n",
    "      reply(\"DELETED\\r\\n\");\n",
    "    }\n",
    "    else\n",
    "    {\n",
    "      reply(\"NOT_FOUND\\r\\n\");\n",
    "    }\n",
    "  }\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Broadcasting"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "羽毛球竞赛系统设计考虑\n",
    "- 弃权处理机制：系统设计时考虑了队员比赛中弃权的情况（如丹麦女队崴脚弃权案例），但未考虑临时换场地需求（如场地不平需更换场地的情况）\n",
    "- 赛制变更影响：原系统基于15分换发球制开发，后羽毛球改为21分制（取消发球权规则），导致系统逻辑完全失效\n",
    "- 硬件配置：采用机械积分牌作为临时解决方案，该问题仅出现在预赛阶段，决赛场地不会发生"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- Unix网络编程: UDP下的IP广播\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "羽毛球竞赛系统结构\n",
    "- 场地配置：每片场地配备显示器和电脑（共7套），中央设置数据库服务器存储比赛进度\n",
    "- 控制终端\n",
    "  - 大屏幕控制电脑（控制两端大屏幕）\n",
    "  - 数据录入电脑（记录队员出场顺序）\n",
    "  - FTP上传电脑（向网易体育频道传输比分数据）\n",
    "- 容错设计\n",
    "  - 比分记录席独立运行，电源中断不影响比赛进行\n",
    "  - 各场地系统相互隔离（如4号场地故障不影响3号场地）\n",
    "- 计分设备演进\n",
    "  - 原计划使用无线PDA，因现场无线干扰改为有线USB连接\n",
    "  - PDA电池续航不足（全天比赛需频繁充电）\n",
    "  - 计分软件统一使用C#开发\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 采用用户层TCP实现发布-订阅（Pub-Sub）模式，替代IP广播（仅UDP支持）;IP广播调试困难，TCP提供端到端可靠性;主从节点冗余设计（如m3/s3配对），避免单点故障"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "积分牌开发系统\n",
    "\n",
    "- 核心机制：采用发布/订阅模式（Pub/Sub）实现比分广播\n",
    "- 数据格式\n",
    "  - Snapshot：完整当前比分（如中国5:2）\n",
    "  - Delta：增量更新（如中国队+1分）\n",
    "- 系统优势\n",
    "  - 组件独立性高，支持单点重启\n",
    "  - 采用Snapshot机制避免历史记录依赖\n",
    "  - 异常处理：PDA和HUB同时故障时从数据库恢复最后比分\n",
    "- 特殊需求实现：为境外转播方提供独立比分接入方案（绕过中文体育频道）"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**分布式系统体会**\n",
    "\n",
    "分布式系统的不可靠性\n",
    "\n",
    "- 典型案例：泰国队抗议事件（对手违反出场顺序规则），导致比赛结果悬而未决\n",
    "- 系统应对：数据库支持未决状态记录，不影响其他比赛正常进行\n",
    "\n",
    "\n",
    "\n",
    "广播机制的选择与实现 \n",
    "\n",
    "- 协议选择\n",
    "  - 放弃IP广播（仅UDP支持，调试困难）\n",
    "  - 采用TCP实现应用层广播（端到端可调试）\n",
    "- 架构设计\n",
    "  - 每个场地独立HUB进程\n",
    "  - 单发布者+多订阅者模式（保证比分修改唯一性）\n",
    "  - 主从备份设计（master-slave配对）"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "系统性能考量\n",
    "\n",
    "- 实时性要求：羽毛球比分变化间隔约几秒（不同于乒乓球的高速变化）\n",
    "- 开发难点\n",
    "  - HUB实现（单点接收，多点分发）\n",
    "  - 协议一致性维护\n",
    "  - 故障恢复机制设计"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 小结"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "> 视频画图, 没理解\n",
    "\n",
    "| 知识点                 | 核心内容                                                     | 易混淆点/难点                                    | 技术实现                                             |\n",
    "| ---------------------- | ------------------------------------------------------------ | ------------------------------------------------ | ---------------------------------------------------- |\n",
    "| 羽毛球比赛系统设计背景 | 系统需处理队员弃权（如丹麦女队崴脚弃权案例）、临时换场地（如地板不平换场地）等意外情况 | 未预见的临时换场地场景需手动调整积分牌           | 预赛阶段机械积分牌处理，决赛阶段无此问题             |\n",
    "| 竞赛规则变更影响       | 原15分换发球制（发球权方得分）改为21分制（得分权与发球方解耦） | 规则变更导致系统逻辑完全失效                     | 软件因规则迭代失去价值，需重构核心逻辑               |\n",
    "| 广播通信架构设计       | 采用用户层TCP实现发布-订阅（Pub-Sub）模式，替代IP广播（仅UDP支持） | IP广播调试困难，TCP提供端到端可靠性              | 主从节点冗余设计（如m3/s3配对），避免单点故障        |\n",
    "| 系统硬件拓扑           | 7片场地各配独立电脑+显示器，数据库服务器管理赛程，大屏幕控制终端 | PDA无线改有线（USB）因现场无线干扰与电池续航问题 | 分布式架构：场地间故障隔离（如4号场地故障不影响3号） |\n",
    "| 数据同步机制           | 仅发布Snapshot比分（非Delta），组件可随时重启获取最新状态    | 依赖PDA本地备份比分，极端情况需数据库恢复        | 弱一致性优先：保证系统可用性而非强实时同步           |\n",
    "| 异常处理案例           | 泰国队抗议对手违规调整出场顺序，导致比赛结果悬而未决         | 数据库需支持“未决状态”，不影响其他比赛数据       | 业务逻辑兼容性设计应对规则外争议                     |\n",
    "| 性能与可靠性权衡       | 比分更新频率低（秒级），无需优化传输性能                     | HUB进程内存存储比分，无持久化需求                | 轻量级协议：TCP调试友好，牺牲广播效率换可控性        |"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 多线程下的性能"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 单机"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 测试muduo版本\n",
    "\n",
    "```shell\n",
    "utop\n",
    "```\n",
    "\n",
    "```shell\n",
    "memcached_debug -t 1\n",
    "```\n",
    "\n",
    "```shell\n",
    "memcached_bench -s -r 30000 # 4个线程, 测试set\n",
    "```\n",
    "\n",
    "```shell\n",
    "memcached_bench -r 30000 # 测试get\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 测试原始版本\n",
    "\n",
    "```shell\n",
    "memcached -m 1024\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "> 要记住测试是过程, 而不是不是结果"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 双机(局域网)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```shell\n",
    "memcached_bench -r 50000\n",
    "```\n",
    "\n",
    "```shell\n",
    "memcached_bench -s -i 10.0.0.37 -t 8\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Benchmarking – requests per second\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- Performance is comparable to original memcached 1.4.22"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Online profiling with gperftools"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## CPU"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- Find hotspots in live production"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 性能测试：测量系统运行速度（如QPS）\n",
    "- 热点分析：定位消耗CPU时间的具体代码段"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "30秒内采集约3000个样本（100Hz采样率）,每个样本记录完整的调用栈信息\n",
    "\n",
    "```shell\n",
    "google-pprof --pdf bin/memcached_debug http://localhost:11212 > set.pdf # 生成set.pdf文件\n",
    "```\n",
    "\n",
    "```shell\n",
    "bin/memcached_bench -t 1 -s\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```shell\n",
    "google-pprof --pdf bin/memcached_debug http://localhost:11212 > get.pdf\n",
    "```\n",
    "\n",
    "```shell\n",
    "bin/memcached_bench -t 1\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "工作原理：\n",
    "- 基于定时中断采集调用栈快照\n",
    "- 统计各函数在采样中的出现频率\n",
    "\n",
    "分析维度：\n",
    "- 绝对耗时：函数在总采样中的占比\n",
    "- 调用关系：通过有向图显示函数调用路径"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 内存"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```shell\n",
    "sudo perf record bin/memcached_debug_tcmalloc -t 1\n",
    "```\n",
    "\n",
    "```shell\n",
    "bin/memcached_bench -t 1\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "perf\n",
    "- 全栈分析: 能够追踪从应用程序到内核的系统调用链\n",
    "- 可视化配合: 需要配合脚本才能实现更漂亮的可视化效果\n",
    "- 负载模拟: 分析时需要先启动负载测试（如100个客户端连接）"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# What’s the overhead?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![6.1](./images/6.1.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Saving memory with boost.intrusive_ptr"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- `g++ -O2 -std=c++11 intrsuiive1.cc -ltcmalloc`\n",
    "\n",
    "![6.2](./images/6.2.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 24 + 24 != 16 + 32"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 只适用于tcmalloc\n",
    "\n",
    "![6.3](./images/6.3.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- GCC 4.8+支持哈希值缓存优化, 必须为哈希函数添加`noexcept`声明, https://gist.github.com/chenshuo/612177b4caf0c1ad7064\n",
    "- GCC 4.7会额外存储哈希值，导致每个节点多8字节开销\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "C++17",
   "language": "C++17",
   "name": "xcpp17"
  },
  "language_info": {
   "name": "C++17"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
